# -*- coding: utf-8 -*-
# created on 2016/05/15

from sympy import sympify, Symbol, fraction, cancel, S

from mathsolver.functions.base.base import BaseFunction, new_latex
from mathsolver.functions.base.objects import BaseEq, BaseValue
from mathsolver.functions.hanshu.dandiao_qiucan import QuJianDiZenQiuCan, QuJianDiJianQiuCan
from mathsolver.functions.hanshu.dingyiyu import QiuDingYiYu
from mathsolver.functions.hanshu.helper import check_inter, check_func, reverse_op, \
    get_rand_from_interval, get_randn_from_interval
from mathsolver.functions.hanshu.zhouqi import has_f
from mathsolver.functions.sympy_utils import get_all_child


def is_abs(expr):
    """是否是绝对值表达式"""
    return "Abs(" in str(expr)


def _cal_var(expr):
    """计算变量"""
    symbols = expr.free_symbols
    if len(symbols) == 2:
        return symbols
    else:
        return set([symb for symb in symbols if 'x' in str(symb) or 'y' in str(symb)])


def is_fenshi(f):
    """判断分式函数"""
    f = cancel(f)
    u, d = fraction(f)
    return d.free_symbols


def _cal_dandiao(expr, var, canshu, canshu_range):
    """特殊值计算单调性，expr 恒单调增/减，与参数取值无关"""
    canshu_val = get_rand_from_interval(canshu_range)
    expr_new = expr.subs(canshu, canshu_val)
    def_field = QiuDingYiYu().solver(BaseEq(['y', expr_new]))
    x1, x2 = get_randn_from_interval(def_field, 2)
    if (expr_new.subs(var, x1) - expr_new.subs(var, x2)) / (x1 - x2) > 0:
        is_zen = True
    else:
        is_zen = False
    return is_zen


class DanDiaoQiuCanGouZhao(BaseFunction):
    """构造函数得单调性求参数范围
    
    输入：
        func_list: 题目给定函数，1个或者2个 
        interval: 定义区间
        ineq_var: optional 变量关系，不如不给定需要设出变量关系
        ineq_rel: 不等式
    
    输出：
        参数范围
    """

    def solver(self, *args):
        # 根据不等式构造函数推导单调性
        infer_dandiao = BuDengShiInferDanDiao(self.known).solver(*args)
        self.steps.extend(infer_dandiao.steps)
        func_expr, is_dizen = infer_dandiao.output[0].value

        # 取件单调求参数范围
        func_list, interval, ineq_var, ineq_rel = args[0], check_inter(args[1]), args[2], args[3]
        if is_dizen:
            dandiao_qiucan = QuJianDiZenQiuCan(self.known).solver(BaseEq(['y', func_expr]), interval)
        else:
            dandiao_qiucan = QuJianDiJianQiuCan(self.known).solver(BaseEq(['y', func_expr]), interval)

        self.steps.extend(dandiao_qiucan.steps)
        self.output = dandiao_qiucan.output
        self.label.add("构造函数得单调性求参数范围")
        return self


class ProofBuDengShiGouZhaoDanDiao(DanDiaoQiuCanGouZhao):
    """构造函数证明不等式"""

    def solver(self, *args):
        super_self = super(ProofBuDengShiGouZhaoDanDiao, self).solver(*args)
        super_self.steps.append(["", "求得的参数范围是题目给定参数范围的子集，得证"])
        self.label = {"构造函数证明不等式"}
        return self


class BuDengShiInferDanDiao(BaseFunction):
    """根据不等式构造函数推导单调性
    
    输入：
        func_list: 题目给定函数，1个或者2个 
        interval: 定义区间
        ineq_var: optional 变量关系，不如不给定需要设出变量关系
        ineq_rel: 不等式
        
    输出：
        函数单调性 
        baseValue((hanshu_expr, is_dizen))
    """

    def solver(self, *args):
        func_list, interval, ineq_var, ineq_rel = args[0], check_inter(args[1]), args[2], args[3]

        # 根据表达式得到单调性
        ineq_rel_l, ineq_rel_op, ineq_rel_r = ineq_rel.value
        ineq_rel_l, ineq_rel_r = sympify(ineq_rel_l), sympify(ineq_rel_r)

        # 计算参数
        # 如果题目没有给定 x1, x2 关系，需要计算变量
        if ineq_var:
            ineq_var_l, ineq_var_op, ineq_var_r = ineq_var.value
            ineq_var_l, ineq_var_r = sympify(ineq_var_l), sympify(ineq_var_r)
        else:
            variables = _cal_var(ineq_rel_l - ineq_rel_r)
            ineq_var_l, ineq_var_op, ineq_var_r = variables.pop(), '>', variables.pop()
            self.steps.append(["", "设 %s %s %s" % (ineq_var_l, ineq_var_op, ineq_var_r)])

        # 绝对值函数，设出变量大小，计算区间单调性
        if is_abs(ineq_rel_l) or is_abs(ineq_rel_r):
            func = check_func(func_list[0])
            dizen = self.cal_dandiaoxing(func)
            k, right_abs = ineq_rel_r.args
            if dizen:
                ineq_rel_l, ineq_rel_r = ineq_rel_l.args[0], k * right_abs.args[0]
                self.steps.append(["", "函数在区间 %s 上单调递增，去绝对值得：%s %s %s" %
                                   (new_latex(interval), new_latex(ineq_rel_l), ineq_rel_op, new_latex(ineq_rel_r))])
            else:
                ineq_rel_l, ineq_rel_r = -ineq_rel_l.args[0], k * right_abs.args[0]
                self.steps.append(["", "函数在区间 %s 上单调递减少，去绝对值得：%s %s %s" %
                                   (new_latex(interval), new_latex(ineq_rel_l), ineq_rel_op, new_latex(ineq_rel_r))])

        # 分式函数，设出变量大小，去分母
        if is_fenshi(ineq_rel_l) or is_fenshi(ineq_rel_r):
            fenshi = ineq_rel_r if is_fenshi(ineq_rel_r) else ineq_rel_l
            fenzhi, fenmu = fraction(cancel(fenshi))
            # 分母为 a*(p - q) 或者 a*(q - p)
            if fenmu / (ineq_var_l - ineq_var_r) < 0:
                ineq_rel_op = reverse_op(ineq_rel_op)

            ineq_rel_l, ineq_rel_r = ineq_rel_l * fenmu, ineq_rel_r * fenmu
            self.steps.append(["", "去分母得 %s %s %s" % (new_latex(ineq_rel_l), ineq_rel_op, new_latex(ineq_rel_r))])

        # 多项式函数，根据表达式得到单调性
        expr_lminusr = ineq_rel_l - ineq_rel_r
        expr_var_r, expr_var_l = expr_lminusr.expand().as_independent(ineq_var_l)
        self.steps.append(["", "由题意知，%s %s %s" % (new_latex(expr_var_l), ineq_rel_op, new_latex(-expr_var_r))])
        expr = expr_var_l.subs(ineq_var_l, Symbol('x'))
        expr2 = expr
        # 替换 expr 里面的 f(x), g(x)，得到最终表达式
        functions = get_all_child(expr, lambda x: has_f(x))
        fname_expr = {f.name: (f.var, f.expression) for f in func_list}
        for function in functions:
            f_name, f_var = function.func, function.args[0]
            _var, _expr = fname_expr[str(f_name)]
            expr2 = expr2.subs(function, _expr.subs(_var, f_var))

        if ineq_rel_op == ineq_var_op:
            self.steps.append(["", "所以函数 %s，即 %s 在 %s 上单调递增" % (new_latex(expr), new_latex(expr2), new_latex(interval))])
            dizen = True
        else:
            self.steps.append(["", "所以函数 %s，即 %s 在 %s 上单调递减" % (new_latex(expr), new_latex(expr2), new_latex(interval))])
            dizen = False

        self.output.append(BaseValue([expr2, dizen]))
        self.label.add("构造函数推导单调性")
        return self

    def cal_dandiaoxing(self, func):
        """计算 func 的单调性"""
        expr, var = func.expression, func.var
        canshu_set = expr.free_symbols - {var}
        canshu = None
        canshu_range = S.Reals
        if canshu_set:
            canshu = canshu_set.pop()
            if self.search(canshu):
                canshu_range = self.search(canshu)
        is_zen = _cal_dandiao(expr, var, canshu, canshu_range)
        return is_zen


if __name__ == '__main__':
    pass
